package logger

import (
	"io"
	"os"
	"reflect"
	"time"

	"github.com/mitchellh/mapstructure"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

// ZapPool ...
type ZapPool struct {
	L          *zap.Logger
	SL         *zap.SugaredLogger
	chanWriter *logChannelWriter
	setting    *LoggerSettingS
}

// NewLogger 获取日志不包括
func NewZapPool(s *LoggerSettingS) *ZapPool {
	var cores []zapcore.Core

	_encodeCfg := getEncodeConfig()
	_consoleEncoder := zapcore.NewConsoleEncoder(_encodeCfg)
	_jsonEncoder := zapcore.NewJSONEncoder(_encodeCfg)

	_levelcheck := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= s.LogLevel
	})

	// 文件输出
	if s.OutFile {
		_logWriter := getLogWriter(s)
		cores = append(cores, zapcore.NewCore(_jsonEncoder, zapcore.AddSync(_logWriter), _levelcheck))
	}

	// 命令行输出
	if s.OutConsole {
		cores = append(cores, zapcore.NewCore(_consoleEncoder, zapcore.AddSync(os.Stdout), _levelcheck))
	}

	// 通过通道输出
	var _chanWriter *logChannelWriter
	if s.OutChannel {
		_chanWriter = NewChannelWriter(s.ChannelBufferLen)
		if s.ChannelEncodeType == "json" {
			cores = append(cores, zapcore.NewCore(_jsonEncoder, zapcore.AddSync(_chanWriter), _levelcheck))
		} else {
			s.ChannelEncodeType = "console"
			cores = append(cores, zapcore.NewCore(_consoleEncoder, zapcore.AddSync(_chanWriter), _levelcheck))
		}

	}

	_core := zapcore.NewTee(cores...)
	_logger := zap.New(_core)

	return &ZapPool{
		L:          _logger,
		SL:         _logger.Sugar(),
		chanWriter: _chanWriter,
		setting:    s,
	}
}

// NewZapPoolC ...
func NewZapPoolC(s *LoggerSettingS) *ZapPool {
	l := NewZapPool(s)
	l.L = l.L.WithOptions(zap.AddCaller())
	l.SL = l.L.Sugar()
	return l
}

func (p *ZapPool) clone() *ZapPool {
	copy := *p
	return &copy
}

// PrintLogSettings 输出日志系统的配置信息
func (p ZapPool) GetLogSettings() *LoggerSettingS {
	return p.setting
}

// GetLogChannel 获取日志的通道
func (p ZapPool) GetLogChannel() <-chan []byte {
	return p.chanWriter.ch
}

// GetLogChannel 获取日志的通道
func (p ZapPool) WithCaller() *ZapPool {
	pp := p.clone()
	pp.L = pp.L.WithOptions(zap.AddCaller())
	pp.SL = pp.L.Sugar()
	return pp
}

// StringToZapLevelHookFunc
// 将字符串转换为 zapcore.Level 类型
// 为 viper 使用
func StringToZapLevelHookFunc() mapstructure.DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		if f.Kind() != reflect.String {
			return data, nil
		}
		if t != reflect.TypeOf(zapcore.DebugLevel) {
			return data, nil
		}

		raw := data.(string)
		if raw == "" {
			return zapcore.InfoLevel, nil
		}

		var level zapcore.Level
		if err := level.UnmarshalText([]byte(raw)); err != nil {
			return nil, err
		}

		return level, nil
	}
}

func getLogWriter(s *LoggerSettingS) io.Writer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   s.FilePath,
		MaxSize:    s.MaxSize,
		MaxBackups: s.MaxBackups,
		MaxAge:     s.MaxAge,
		Compress:   false,
	}
	return lumberJackLogger
}

func getEncodeConfig() zapcore.EncoderConfig {
	return zapcore.EncoderConfig{
		MessageKey:  "msg",
		LevelKey:    "level",
		EncodeLevel: zapcore.CapitalLevelEncoder,
		TimeKey:     "ts",
		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendString(t.Format("2006-01-02 15:04:05"))
		},
		CallerKey:    "file",
		EncodeCaller: zapcore.ShortCallerEncoder,
		EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendInt64(int64(d) / 1000000)
		},
	}
}
